package org.budo.phone.freeswitch;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;

import org.budo.phone.freeswitch.util.EslUtil;
import org.budo.support.java.net.util.JavaNetUtil;
import org.budo.support.lang.util.StringUtil;
import org.budo.time.Time;
import org.freeswitch.esl.client.IEslEventListener;
import org.freeswitch.esl.client.inbound.Client;
import org.freeswitch.esl.client.transport.event.EslEvent;
import org.freeswitch.esl.client.transport.message.EslMessage;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;

/**
 * @author lmw
 */
@Slf4j
@Getter
@Setter
@ToString
@NoArgsConstructor
public class FreeswitchEslClient implements IEslEventListener {
    private String host;

    private Integer port;

    private String password;

    /**
     * 消息先由自己收到，再转发给这个listener
     */
    private IEslEventListener eslEventListener;

    /**
     * 最后收到消息时间，每次收到消息时更新
     */
    private Timestamp lastMessageTime;

    private Client _eslClient;

    public FreeswitchEslClient(String host, Integer port, String password, IEslEventListener eslEventListener) {
        this.setHost(host);
        this.setPort(port);
        this.setPassword(password);

        this.setEslEventListener(eslEventListener);
    }

    @Override
    public void eventReceived(EslEvent event) {
        this.lastMessageTime = Time.now().toTimestamp();
        this.getEslEventListener().eventReceived(event);
    }

    @Override
    public void backgroundJobResultReceived(EslEvent event) {
        this.lastMessageTime = Time.now().toTimestamp();
        this.getEslEventListener().backgroundJobResultReceived(event);
    }

    public Boolean isInReg(String authUser) {
        EslMessage sendSyncApiCommand = this.sendSyncApiCommand("sofia status profile internal reg", "");

        List<String> bodyLines = sendSyncApiCommand.getBodyLines();
        String lines = StringUtil.join(bodyLines);
        lines = lines.replaceAll(" ", "").replaceAll("\t", "");
        return lines.contains(",Auth-User:" + authUser + ",");
    }

    public List<String> queueNames() {
        EslMessage sendSyncApiCommand = this.sendSyncApiCommand("callcenter_config queue list", "");

        List<String> queues = new ArrayList<String>();

        List<String> bodyLines = sendSyncApiCommand.getBodyLines();
        for (int i = 1; i < bodyLines.size() - 1; i++) { // 舍弃首行头，舍弃最后一行的+OK
            String line = bodyLines.get(i);
            int a = line.indexOf('|');

            String queue = line.substring(0, a);
            queues.add(queue);
        }
        return queues;
    }

    public EslMessage sendSyncApiCommand(String command, String arg) {
        this.assertInited();

        log.info("#79 sendSyncApiCommand " + command + " " + arg);
        return _eslClient.sendSyncApiCommand(command, arg);
    }

    /**
     * 连接ESL，如果已连接就直接返回，判断假死重连
     */
    public void assertInited() {
        if (Time.when(this.getLastMessageTime()).isBefore(Time.now().minusSecond(20))) { // 最后消息事件在20秒以前
            log.error("#68 lastMessageTime=" + this.getLastMessageTime() + ", _eslClient=" + this._eslClient + ", set _eslClient=null, init it again");
            this._eslClient = null; // 重连
        }

        if (null != this._eslClient) {
            return;
        }

        synchronized (this) {
            if (null != this._eslClient) {
                return;
            }

            if (!JavaNetUtil.isPortUsing(this.getHost(), this.getPort())) {
                log.error("#88 host=" + this.getHost() + ", port=" + this.getPort() + " is not ok");
                return;
            }

            final int timeout = 1; // Seconds

            this._eslClient = EslUtil.connectEslClient(this.getHost(), //
                    this.getPort(), //
                    this.getPassword(), //
                    timeout, //
                    this); // 这里用自己实现接口方法收消息，再转给传入的listener，方便以eslClient为单位做超时重连
        }
    }

    public void close() {
        log.info("#34 close, this=" + this);
        this._eslClient.close();
    }
}